package com.zmzncs.lmtc.module.order.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.Maps;
import com.zmzncs.lmtc.common.pojo.daoer.CarPortRes;
import com.zmzncs.lmtc.common.pojo.daoer.MonthlyCarInfoRes;
import com.zmzncs.lmtc.common.pojo.tengda.CurrFeeForm;
import com.zmzncs.lmtc.common.pojo.tengda.TengdaForm;
import com.zmzncs.lmtc.common.util.BolianUtil;
import com.zmzncs.lmtc.common.util.MpMessageUtil;
import com.zmzncs.lmtc.common.pojo.CurrOrderRes;
import com.zmzncs.lmtc.common.pojo.LoginUser;
import com.zmzncs.lmtc.common.pojo.bolian.*;
import com.zmzncs.lmtc.common.pojo.daoer.CarFeeRes;
import com.zmzncs.lmtc.common.pojo.enums.PayStatusEnum;
import com.zmzncs.lmtc.common.pojo.enums.PayTypeEnum;
import com.zmzncs.lmtc.common.pojo.enums.optionalvalue.order.OrderCardTypeEnum;
import com.zmzncs.lmtc.common.pojo.enums.optionalvalue.order.OrderStatusEnum;
import com.zmzncs.lmtc.common.pojo.enums.optionalvalue.order.OutTypeEnum;
import com.zmzncs.lmtc.common.util.*;
import com.zmzncs.lmtc.module.customer.dao.CarDao;
import com.zmzncs.lmtc.module.customer.dao.CardDao;
import com.zmzncs.lmtc.module.customer.dao.CardMonthRecordDao;
import com.zmzncs.lmtc.module.customer.dao.CustomerDao;
import com.zmzncs.lmtc.module.customer.pojo.car.CarDTO;
import com.zmzncs.lmtc.module.customer.pojo.card.Card;
import com.zmzncs.lmtc.module.customer.pojo.card.HasCardDTO;
import com.zmzncs.lmtc.module.customer.pojo.card.CardHasForm;
import com.zmzncs.lmtc.module.customer.service.CardService;
import com.zmzncs.lmtc.module.finance.service.CapitalRecordService;
import com.zmzncs.lmtc.module.order.dao.OrderCardDao;
import com.zmzncs.lmtc.module.order.dao.OrderInfoDao;
import com.zmzncs.lmtc.module.order.dao.OrderTradeRecordDao;
import com.zmzncs.lmtc.module.order.dao.ParkReserveRecordDao;
import com.zmzncs.lmtc.module.order.pojo.order_card.OrderCard;
import com.zmzncs.lmtc.module.order.pojo.order_info.OrderInfo;
import com.zmzncs.lmtc.module.order.pojo.order_info.OrderInfoDTO;
import com.zmzncs.lmtc.module.order.pojo.order_info.UserOrderInfoForm;
import com.zmzncs.lmtc.module.order.pojo.order_trade_record.OrderTradeRecord;
import com.zmzncs.lmtc.module.order.pojo.order_trade_record.OrderTradeRecordDTO;
import com.zmzncs.lmtc.module.order.service.OrderInfoService;
import com.zmzncs.lmtc.module.order.service.ParkReserveRecordService;
import com.zmzncs.lmtc.module.park.dao.ParkInfoDao;
import com.zmzncs.lmtc.module.park.pojo.parkinfo.ParkInfo;
import com.zmzncs.lmtc.module.park.pojo.parkinfo.ParkInfoDTO;
import com.zmzncs.lmtc.module.shop.service.ShopTicketService;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 客户-信息 服务实现类
 * </p>
 *
 * @author 任建波
 * @since 2020-09-01
 */
@Slf4j
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoDao, OrderInfo> implements OrderInfoService {

    @Autowired
    OrderInfoDao orderInfoDao;
    @Autowired
    OrderTradeRecordDao orderTradeRecordDao;
    @Autowired
    CapitalRecordService capitalRecordService;
    @Autowired
    CardService cardService;
    @Autowired
    CarDao carDao;
    @Autowired
    @Lazy
    OrderInfoService orderInfoService;
    @Autowired
    ParkInfoDao parkInfoDao;
    @Autowired
    CustomerDao customerDao;
    @Autowired
    OrderCardDao orderCardDao;
    @Autowired
    CardDao cardDao;
    @Autowired
    MpMessageUtil mpMessageUtil;
    @Autowired
    ParkReserveRecordDao parkReserveRecordDao;
    @Autowired
    ParkReserveRecordService parkReserveRecordService;
    @Autowired
    CardMonthRecordDao cardMonthRecordDao;
    @Autowired
    ShopTicketService shopTicketService;


    @Autowired
    HttpServletRequest request;

    /**
     * 查询客户当前车辆在停车场情况
     */
    @Override
    public List<OrderInfoDTO> getCustomerCarParkList() {
        LoginUser loginUser = JwtUtil.getLoginUser(request);
        List<OrderInfoDTO> orderInfoDTOList = orderInfoDao.getCustomerCarParkList(loginUser.getCustomerId());

        return orderInfoDTOList;
    }

    /**
     * 查询客户订单
     */
    @Override
    public IPage<OrderInfoDTO> getCustomerOrderPage(UserOrderInfoForm form) {
        LoginUser loginUser = JwtUtil.getLoginUser(request);
        Page page = new Page(form.getCurrentPage(), form.getPageSize());
        form.setCustomerId(loginUser.getCustomerId());
        IPage<OrderInfoDTO> customerOrderPage = orderInfoDao.getCustomerOrderList(page,form);
        // 支付成功的交易记录
        for (OrderInfoDTO orderInfoDTO : customerOrderPage.getRecords()) {
            List<OrderTradeRecordDTO> paidOrderTradeRecord = orderTradeRecordDao.getPaidOrderTradeRecordByOrderCode(orderInfoDTO.getOrderCode());
            StringBuilder payType = new StringBuilder();
            //  没有交易记录，并且有停车场月租（停车场月租有可能是夜间白天卡，也会产生交易记录）
            if(paidOrderTradeRecord.size() == 0 && orderInfoDTO.getIsParkMonthCar().equals(1)){
                payType = new StringBuilder("【停车场月租】");
            } else  {
                if(orderInfoDTO.getIsDayMonthCar().equals(1)) {
                    payType.append("【").append("白天卡").append("】");
                }
                if(orderInfoDTO.getIsDayMonthCar().equals(2)) {
                    payType.append("【").append("夜间卡").append("】");
                }
                for (OrderTradeRecordDTO tradeRecordDTO : paidOrderTradeRecord) {
                    if (tradeRecordDTO.getPayType() != null) {
                        PayTypeEnum payTypeEnum = EnumUtil.getByCode(tradeRecordDTO.getPayType(), PayTypeEnum.class);
                        payType.append("【").append(payTypeEnum.getMassage()).append("】");
                    }
                }
            }

            orderInfoDTO.setPayType(payType.toString());
        }

        return customerOrderPage;
    }

    /**
     * 根据交易流水号查询订单是否支付成功
     */
    @Override
    public boolean getOrderTradeRecordByTradeNo(String orderCode, String tradeNo) {
        for (int i = 0; i < 10; i++) {
            log.info("预支付订单号【{}】流水号【{}】是否支付成功，第【{}】查询", orderCode, tradeNo, i);
            //  根据订单id和流水号查询订单交易记录
            OrderTradeRecordDTO orderTradeRecordDTO = orderTradeRecordDao.getOrderTradeRecordByOrderCodeTradeNo(orderCode, tradeNo);
            if (orderTradeRecordDTO.getPayStatus().equals(PayStatusEnum.PAID.getCode())) {
                return true;
            }

            //  睡眠1秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        log.error("预支付订单号【{}】流水号【{}】10秒无回调", orderCode, tradeNo);

        return false;
    }

    /**
     * 查询车主实时订单金额
     */
    @Override
    public CurrOrderRes queryCurrOrder(String orderCode, String carNumber, Long parkId) {
        CurrOrderRes currOrderRes = new CurrOrderRes();

        //  查询停车场信息
        ParkInfo parkInfo = parkInfoDao.selectById(parkId);
        //  查询泊链实时订单
        BolianCurrOrderRes bolianCurrOrderRes = BolianUtil.quercurrorder(orderCode, carNumber, parkInfo.getBolianParkUnionId(), parkInfo.getBolianParkId());
        bolianCurrOrderRes = bolianCurrOrderRes == null ? new BolianCurrOrderRes() : bolianCurrOrderRes;
        BeanUtils.copyProperties(bolianCurrOrderRes, currOrderRes);

        /*1、检验是否有会员卡*/
        CardHasForm cardHasForm = new CardHasForm();
        cardHasForm.setCarNumber(carNumber);
        cardHasForm.setParkId(parkId);
        cardHasForm.setAmount( bolianCurrOrderRes.getMoney() == null ? new BigDecimal(0L) : new BigDecimal(bolianCurrOrderRes.getMoney()) );   //  金额
        HasCardDTO hasCardDTO = cardService.hasCard(cardHasForm);

        currOrderRes.setCardType(hasCardDTO.getOrderCardTypeEnum().getCode());  //  会员卡类型（1：月卡，2：时长卡 ，3：充值，100：白天卡，110：夜间卡，0：没有使用会员卡）
        currOrderRes.setCardTypeMsg(hasCardDTO.getOrderCardTypeEnum().getMassage());    //  会员卡类型描述（月租卡，时长卡，充值卡，没有使用会员卡）
        /*2、判断是否可用预支付*/
//        if (hasCardDTO.getOrderCardTypeEnum().equals(OrderCardTypeEnum.NO)) {
//            currOrderRes.setCanPrePay(2);   //  是否展示离场按钮（0：不展示，1：展示，2：不可预支付（余额不足））
//            currOrderRes.setErrmsg("余额不足");
//            currOrderRes.setCardType(OrderCardTypeEnum.BALANCE.getCode());  //  会员卡类型（1：月卡，2：时长卡 ，3：充值，100：白天卡，110：夜间卡，0：没有使用会员卡）
//            currOrderRes.setCardTypeMsg("余额不足");    //  会员卡类型描述（月租卡，时长卡，充值卡，没有使用会员卡）
//        } else {
//            currOrderRes.setCanPrePay(1);   //  是否展示离场按钮（0：不展示，1：展示，2：不可预支付（余额不足））
//        }
        //  非泊链停车场不可预支付
        if(!parkInfo.getParkSource().equals(1)){
            currOrderRes.setCanPrePay(0);   //  是否展示离场按钮（0：不展示，1：展示，2：不可预支付（余额不足））
            currOrderRes.setErrmsg("非泊链停车场不可预支付");
        //  泊链停车场可预支付
        } else {
            currOrderRes.setCanPrePay(1);   //  是否展示离场按钮（0：不展示，1：展示，2：不可预支付（余额不足））
            currOrderRes.setErrmsg("泊链停车场可预支付");
        }

        /*3、查询实时订单失败，从数据库查询进场时间*/
        if (currOrderRes.getState() == null || !currOrderRes.getState().equals(1)) {
            OrderInfoDTO orderInfoDTO = orderInfoDao.getByOrderCode(orderCode);
            currOrderRes.setDuration(DateTimeUtil.betweenMinute(new Date(), orderInfoDTO.getInTime()));
            currOrderRes.setStart_time(orderInfoDTO.getInTime().getTime());
            currOrderRes.setErrmsg(bolianCurrOrderRes.getErrmsg());
            if (currOrderRes.getErrmsg() == null) {
                log.error("查询车牌【" + carNumber + "】的实时订单【" + orderCode + "】错误");
                currOrderRes.setErrmsg("查询车牌【" + carNumber + "】的实时订单【" + orderCode + "】错误");
            }
        }

        currOrderRes.setParkSource(parkInfo.getParkSource());  //  车场来源（1：泊链 2：咪网 3: 道尔）
        currOrderRes.setPark_name(parkInfo.getParkName());  //  停车场名称

        return currOrderRes;
    }

    /**
     * 泊链请求查询道尔、盒子等的实时订单
     */
    @Override
    public BolianParkCurrOrderRes getParkCurrOrder(String params) {
        BolianParkCurrOrderRes bolianParkCurrOrderRes = new BolianParkCurrOrderRes();

        JsonNode jsonNode = JsonUtil.getJsonNode(params);
        String dataStr = jsonNode.get("data").toString();
        BolianParkCurrOrderDataForm form = JSONObject.parseObject(dataStr, BolianParkCurrOrderDataForm.class);

        //  停车场
        ParkInfoDTO parkInfoDTO = parkInfoDao.getByBolianParkId(form.getPark_id());

        //  道尔实时订单
        CarFeeRes carFeeRes = new CarFeeRes();
        if(parkInfoDTO.getParkSource().equals(3)){
            carFeeRes = DaoerApiUtil.getcarfee(form.getPark_id(), form.getCar_number());
        }
        //  腾达实时订单
        if(parkInfoDTO.getParkSource().equals(4)){
            TengdaForm tengdaForm = TengdaUtil.queryCurrOrder(form.getPark_id(), form.getCar_number());

            CarFeeRes.Head head = new CarFeeRes.Head();
            head.setStatus(tengdaForm.getCode());
            head.setMessage(tengdaForm.getServiceId());
            carFeeRes.setHead(head);

            if(tengdaForm.getCode().equals(1)){
                CurrFeeForm currFeeForm = JSONObject.toJavaObject(tengdaForm.getDataItems().get(0), CurrFeeForm.class);

                CarFeeRes.Body body = new CarFeeRes.Body();
                body.setAmount(new Double(currFeeForm.getPaidFee()));
                body.setCarNo(currFeeForm.getPlateNumber());
                body.setChargeDuration(new Long(currFeeForm.getUseTime()) / 60);
                body.setInTime(currFeeForm.getEnterTime());
                body.setPayCharge(new Double(currFeeForm.getPaidFee()));
                carFeeRes.setBody(body);
            }
        }

        //  处理查询结果
        if (DaoerApiUtil.SUCCESS_STATUS.equals(carFeeRes.getHead().getStatus()) && carFeeRes.getBody() != null) {
            bolianParkCurrOrderRes.setState(1);
//            Long queryTime = DateTimeUtil.fomatDateTime(carFeeRes.getBody().getChargeTime()).getTime() / 1000;
            Long queryTime = new Date().getTime() / 1000;
            bolianParkCurrOrderRes.setQuery_time(String.valueOf(new Date().getTime() / 1000));
            Long chargeDuration = carFeeRes.getBody().getChargeDuration();
            bolianParkCurrOrderRes.setDuration(chargeDuration.intValue());
            bolianParkCurrOrderRes.setPark_id(form.getPark_id());
            bolianParkCurrOrderRes.setOrder_id(form.getOrder_id());
            bolianParkCurrOrderRes.setQuery_order_no(form.getQuery_order_no());
            Double discountAmount = carFeeRes.getBody().getDiscountAmount();
            if (discountAmount == null){
                discountAmount = 0d;
            }
            bolianParkCurrOrderRes.setDerate_money(discountAmount.toString());
            Double payCharge = carFeeRes.getBody().getPayCharge();
            if (payCharge == null){
                payCharge = 0d;
            }
            bolianParkCurrOrderRes.setPrice(payCharge.toString());
            bolianParkCurrOrderRes.setTotal(payCharge.toString());
            bolianParkCurrOrderRes.setCar_number(form.getCar_number());
            long inTime = Objects.requireNonNull(DateTimeUtil.fomatDateTime(carFeeRes.getBody().getInTime())).getTime() / 1000;
            bolianParkCurrOrderRes.setIn_time(Long.toString(inTime));
            bolianParkCurrOrderRes.setMultiple_prepay("1");
        } else {
            //  订单信息
            bolianParkCurrOrderRes.setState(0);
            bolianParkCurrOrderRes.setErrmsg(carFeeRes.getHead().getMessage());
        }

        log.info("泊链请求停车场实时订单响应数据：{}", bolianParkCurrOrderRes);
        return bolianParkCurrOrderRes;
    }

    /**
     * 预支付
     */
    @Override
    @Transactional
    public BolianPrePayRes prePayCurrOrder(String orderCode) {
        //  交易流水号
        String tradeNo = BolianUtil.getTradeNo();
        //  登录客户信息
        LoginUser loginUser = JwtUtil.getLoginUser(request);

        //  查询订单信息
        OrderInfoDTO orderInfoDTO = orderInfoDao.getByOrderCode(orderCode);
        if (orderInfoDTO == null) {
            ExceptionUtil.businessException("订单号【" + orderCode + "】错误");
        }
        if (orderInfoDTO.getStatus().equals(OrderStatusEnum.OUT.getCode())) {
            ExceptionUtil.businessException("车辆已支付已出场，不能再预支付");
        }
        //  查询停车场详情
        ParkInfo parkInfo = parkInfoDao.selectById(orderInfoDTO.getParkId());

        /*3、查询泊链实时订单*/
        CurrOrderRes bolianCurrOrderRes = orderInfoService.queryCurrOrder(orderCode, orderInfoDTO.getCarNumber(), orderInfoDTO.getParkId());
        //  查询泊链实时订单失败
        if (!bolianCurrOrderRes.getState().equals(1)) {
            ExceptionUtil.businessException(bolianCurrOrderRes.getErrmsg());
        }
        if (new BigDecimal(bolianCurrOrderRes.getMoney()).compareTo(new BigDecimal(0)) == 0) {
            ExceptionUtil.businessException("当前未产生费用，不需要支付");
        }

        /*4、判断会员卡*/
        CardHasForm cardHasForm = new CardHasForm();
        cardHasForm.setCarNumber(orderInfoDTO.getCarNumber());
        cardHasForm.setParkId(orderInfoDTO.getParkId());
        cardHasForm.setAmount(new BigDecimal(bolianCurrOrderRes.getMoney()));
        cardHasForm.setFreeOutTime(bolianCurrOrderRes.getFree_out_time().toString());
        cardHasForm.setBeginTime(orderInfoDTO.getInTime());
        cardHasForm.setEndTime(new Date());
        HasCardDTO hasCardDTO = cardService.hasCard(cardHasForm);
        //  余额不足，不能预支付
        if (hasCardDTO.getOrderCardTypeEnum().equals(OrderCardTypeEnum.NO)) {
            ExceptionUtil.businessException("余额不足，不能预支付");
        }

        //  如果已经开始分润，计算支付停车场金额
        BigDecimal paidParkAmount = new BigDecimal(bolianCurrOrderRes.getMoney());
        if(parkInfo.getOrderDivideBeginTime() != null && new Date().getTime() >= parkInfo.getOrderDivideBeginTime().getTime()){
            if(parkInfo.getOrderDivide() != null) {
                //  订单分润金额（支付金额 * 订单分润比率）
                BigDecimal orderDivideAmount = paidParkAmount.multiply( parkInfo.getOrderDivide().divide(new BigDecimal(100)));
                //  支付停车场月租金额（停车场月租金额 - 月租分润金额）
                paidParkAmount =  paidParkAmount.subtract(orderDivideAmount);
            } else {
                log.error("缺少分润比率，无法分润");
            }
        }

        /*4、发起泊链预支付*/
        BolianPrePayRes bolianPrePayRes = BolianUtil.prePay(orderCode, tradeNo, orderInfoDTO.getCarNumber(), parkInfo.getBolianParkUnionId(), parkInfo.getBolianParkId(), paidParkAmount.toString(), loginUser.getCustomerId());

        /*5、添加预支付订单交易记录*/
        OrderTradeRecord orderTradeRecord = new OrderTradeRecord();
        orderTradeRecord.setPayType(hasCardDTO.getOrderCardTypeEnum().getCode());   //  支付类型（1：月租卡；2：白天卡；3：夜间卡；4：余额；5：现金；99：其他）
        orderTradeRecord.setPayStatus(!bolianPrePayRes.getState().equals(1) ? PayStatusEnum.FAIL.getCode() : PayStatusEnum.PAID.getCode());    //  支付状态（0：待支付，1：已支付，2：取消支付）
        orderTradeRecord.setPaidAmount(new BigDecimal(bolianCurrOrderRes.getMoney()));   //  实付金额
        orderTradeRecord.setPayableAmount(new BigDecimal(bolianCurrOrderRes.getMoney()));  //  应付金额
        orderTradeRecord.setOrderDivide(parkInfo.getOrderDivide());
        orderTradeRecord.setPaidParkAmount(paidParkAmount);
        orderTradeRecord.setFreeOutTime(bolianCurrOrderRes.getFree_out_time()); //  预付后免费出场时长（分钟）
        orderTradeRecord.setPayMsg(bolianPrePayRes.getErrmsg());
        orderTradeRecord.setOrderCode(orderInfoDTO.getOrderCode()); //  订单号
        orderTradeRecord.setTradeNo(tradeNo);   //  交易流水号
        orderTradeRecord.setPayTime(new Date());    //  支付时间
        orderTradeRecord.setTradeType(1);   //  交易类型（1：预支付，2：出场支付，3：不确定（补录的交易记录））
        orderTradeRecord.setPayMsg("预支付");
        orderTradeRecordDao.insert(orderTradeRecord);

        //  添加使用的会员卡
        for(HasCardDTO.UsedCard usedCard: hasCardDTO.getUsedCardList()){
            OrderCard orderCard = new OrderCard();
            orderCard.setCardId(usedCard.getId());
            orderCard.setOrderCode(cardHasForm.getOrderCode());
            orderCard.setTradeRecordId(orderTradeRecord.getId());
            orderCardDao.insert(orderCard);
        }

        /*6、支付成功*/
        if (bolianPrePayRes.getState().equals(1)) {
            /*7、如果是余额支付，则扣除余额*/
            if(hasCardDTO.getOrderCardTypeEnum().equals(OrderCardTypeEnum.BALANCE)){
                for (HasCardDTO.UsedCard usedCard : hasCardDTO.getUsedCardList()) {
                    Card card = new Card();
                    card.setId(usedCard.getId());
                    card.setCardBalance(usedCard.getOldBalance().subtract(new BigDecimal(bolianCurrOrderRes.getMoney())));
                    cardDao.updateById(card);
                    log.info("预支付-扣除余额【{}】元", bolianCurrOrderRes.getMoney());
                }
            }
        }

        return bolianPrePayRes;
    }

    /**
     * 泊链停车场预支付通知
     */
    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED)    //  READ_COMMITTED读已提交，不可重读
    public Map prePayOrderNotice(String data) {
        JsonNode jsonNode = JsonUtil.getJsonNode(data);
        String dataStr = jsonNode.get("data").toString();

        //  通知中萌数据（客户向泊链停车场支付）
        BoLianPrePayOrderDataForm zmFormData = JSONObject.parseObject(dataStr, BoLianPrePayOrderDataForm.class, Feature.OrderedField);

        Map<String, Object> map = new HashMap();
        map.put("order_id", zmFormData.getOrder_id());    //  订单记录号(车辆在停车场停车唯一订单编号，对应入场订单编号)
        map.put("trade_no", zmFormData.getTrade_no());    //  交易单号（电子支付对账）
        map.put("state", 1);
        map.put("prepay", zmFormData.getPrepay());
//        map.put("park_id", parkFormData.getPark_id());
        map.put("errmsg", "处理完成");

        return map;
    }

    /**
     * 验证是否支持支付无感支付
     */
    @Override
    @Transactional
    public Map<String, Object> verifyNosencePay(String data) {
        Map<String, Object> map = Maps.newHashMap();
        JsonNode jsonNode = JsonUtil.getJsonNode(data);

        //  用户编号，车场编号，车牌，进场时间，金额
        CarOutPayDataForm outPayDataForm = JSONObject.parseObject(jsonNode.get("data").toString(), CarOutPayDataForm.class);
        //  获取停车场信息
        ParkInfoDTO parkInfoDTO = parkInfoDao.getParkByBolianParkUnionIdBolianParkId(outPayDataForm.getUnion_id(), outPayDataForm.getPark_id());
        if(parkInfoDTO == null) {
            log.error( "停车场不存在");
            map.put("state", 0);
            map.put("errmsg", "停车场不存在");
            return map;
        }
        if (!parkInfoDTO.getIsAutopay().equals(1)) {
            log.error("不支持无感支付");
            map.put("state", 0);
            map.put("errmsg", "不支持无感支付");
            return map;
        }

        /*2、校验是否有会员卡*/
        CardHasForm cardHasForm = new CardHasForm();
        cardHasForm.setParkId(parkInfoDTO.getId());
        cardHasForm.setCarNumber(outPayDataForm.getPlate_number());
        cardHasForm.setAmount(outPayDataForm.getAmount());
        cardHasForm.setFreeOutTime(outPayDataForm.getFreeOutTime());
        cardHasForm.setBeginTime(DateTimeUtil.stampToTime(outPayDataForm.getIn_time()));
        cardHasForm.setEndTime(new Date());
        //  是否有会员卡
        HasCardDTO hasCardDTO = cardService.hasCard(cardHasForm);

        /*3、检验是否无感抬杆*/
        if(!hasCardDTO.getOrderCardTypeEnum().equals(OrderCardTypeEnum.NO)){
            map.put("state", "1");
            map.put("errmsg", "无感抬杆");
        } else {
            log.info(hasCardDTO.getMassage());
            map.put("state", "0");
            map.put("errmsg", "没有会员卡，禁止放行");
        }

        return map;
    }

    /**
     * 非泊链出场判断是否无感抬杆
     */
    @Override
    @Transactional
    public Map<String, Object> carOutPay(String data) {
        Map<String, Object> map = Maps.newHashMap();
        JsonNode jsonNode = JsonUtil.getJsonNode(data);

        //  用户编号，车场编号，车牌，进场时间，金额
        CarOutPayDataForm outPayDataForm = JSONObject.parseObject(jsonNode.get("data").toString(), CarOutPayDataForm.class);
        //  查询该订单是否已经有出场支付
        List<OrderTradeRecordDTO> paidOrderTradeRecord = orderTradeRecordDao.getOutPayByOrderCode(outPayDataForm.getOrder_id());
        if(paidOrderTradeRecord.size() > 0){
            log.warn("无感抬杆-订单【{}】已经有出场支付记录", outPayDataForm.getOrder_id());
            map.put("state", "1");
            map.put("errmsg", "无感抬杆-已经有出场支付记录");
            return map;
        }
        //  获取停车场信息
        ParkInfoDTO parkInfoDTO = parkInfoDao.getParkByBolianParkUnionIdBolianParkId(outPayDataForm.getUnion_id(), outPayDataForm.getPark_id());
        if (parkInfoDTO == null) {
            log.error("停车场不存在");
            map.put("state", 0);
            map.put("errmsg", "停车场不存在");
            return map;
        }
        if (!parkInfoDTO.getIsAutopay().equals(1)) {
            log.error("不支持无感支付");
            map.put("state", 0);
            map.put("errmsg", "不支持无感支付");
            return map;
        }

        /*1、查询客户该车牌最后未出场订单*/
        OrderInfoDTO orderInfoDTO = orderInfoDao.getLastNotOutOrderByCarNumber(outPayDataForm.getPlate_number());
        //  添加缺少的进场订单
        if (orderInfoDTO == null) {
            //  查询实时订单
            BolianCurrOrderRes quercurrorder = BolianUtil.quercurrorder(outPayDataForm.getOrder_id(), outPayDataForm.getPlate_number(), parkInfoDTO.getBolianParkUnionId(), parkInfoDTO.getBolianParkId());
            if(quercurrorder == null || quercurrorder.getState() == null){
                log.error("车牌号【{}】没有在场记录，并且查询实时订单失败", outPayDataForm.getPlate_number());
                map.put("state", "0");   //0支付失败 1支付成功
                map.put("errmsg", "车牌号" + outPayDataForm.getPlate_number() + "没有在场记录，并且查询实时订单失败");
                return map;
            }
            if (quercurrorder.getState().equals(1)) {
                log.error("车牌号【{}】没有在场记录，添加异常进场订单。", outPayDataForm.getPlate_number());
                //  添加缺少的进场订单
                Long inTime = outPayDataForm.getIn_time() == null ? new Date().getTime() : outPayDataForm.getIn_time();
                orderInfoDTO = addExceptionInOrder(quercurrorder.getOrder_id(), outPayDataForm.getUser_id(), outPayDataForm.getPlate_number(), parkInfoDTO, parkInfoDTO.getBolianComId(), inTime, new Date().getTime(), OrderStatusEnum.IN);
            } else {
                log.error("车牌号【{}】没有在场记录。", outPayDataForm.getPlate_number());
                map.put("state", "0");   //0支付失败 1支付成功
                map.put("errmsg", "车牌号" + outPayDataForm.getPlate_number() + "没有在场记录");
                return map;
            }
        }

        /*2、校验是否有会员卡*/
        CardHasForm cardHasForm = new CardHasForm();
        cardHasForm.setParkId(parkInfoDTO.getId());
        cardHasForm.setCarNumber(outPayDataForm.getPlate_number());
        cardHasForm.setAmount(outPayDataForm.getAmount());
        cardHasForm.setOrderCode(orderInfoDTO.getOrderCode());
        cardHasForm.setFreeOutTime(outPayDataForm.getFreeOutTime());
        cardHasForm.setBeginTime(DateTimeUtil.stampToTime(outPayDataForm.getIn_time()));
        cardHasForm.setEndTime(new Date());
        //  是否有会员卡
        HasCardDTO hasCardDTO = cardService.hasCard(cardHasForm);
        OrderCardTypeEnum orderCardTypeEnum = hasCardDTO.getOrderCardTypeEnum();

        /*3、检验是否无感抬杆*/
        if (!orderCardTypeEnum.equals(OrderCardTypeEnum.NO)) {
            map.put("state", "1");
            map.put("errmsg", "无感抬杆");
            String tradeNo = BolianUtil.getTradeNo();
            //  泊链预支付
            BolianPrePayRes bolianPrePayRes = BolianUtil.prePay(orderInfoDTO.getOrderCode(), tradeNo, outPayDataForm.getPlate_number(), parkInfoDTO.getBolianParkUnionId(), parkInfoDTO.getBolianParkId(), outPayDataForm.getAmount().toString(), hasCardDTO.getCustomerId());
            //  预支付成功
            if (bolianPrePayRes.getState().equals(1)) {
                /*5、添加订单交易记录*/
                OrderTradeRecord orderTradeRecord = new OrderTradeRecord();
                orderTradeRecord.setPayType(orderCardTypeEnum.getCode());   //  支付类型（1：月租卡；2：白天卡；3：夜间卡；4：余额；5：现金；99：其他）
                orderTradeRecord.setPayableAmount(cardHasForm.getAmount());  //  应付金额
                orderTradeRecord.setPaidAmount(cardHasForm.getAmount());   //  实付金额
                String freeOutTime = cardHasForm.getFreeOutTime();
                orderTradeRecord.setFreeOutTime(freeOutTime != null ? Integer.parseInt(freeOutTime) : null); //  免费出场时长（分钟）
                orderTradeRecord.setPayStatus(PayStatusEnum.PAID.getCode());    //  支付状态（0：待支付，1：已支付，2：取消支付）
                orderTradeRecord.setOrderCode(cardHasForm.getOrderCode()); //  订单号
                orderTradeRecord.setTradeNo(tradeNo);   //  交易流水号
                orderTradeRecord.setPayTime(new Date());    //  支付时间
                orderTradeRecord.setTradeType(2);   //  交易类型（1：预支付，2：出场支付，3：不确定（补录的交易记录））
                orderTradeRecord.setPayMsg("出场支付");
                orderTradeRecordDao.insert(orderTradeRecord);

                //查询用户是否购买特殊月卡（白间卡、夜间卡）
//            List<CardMonthRecordDTO> cardMonthRecordList = cardMonthRecordDao.getMonthCardByParkIdCarNumber(cardHasForm.getParkId(),cardHasForm.getCarNumber());
//
//            if (cardMonthRecordList != null && cardMonthRecordList.size() > 0){
//                CardMonthRecordDTO cardMonthRecordDTO = cardMonthRecordList.get(0);
//                String monthType = cardMonthRecordDTO.getMonthType();
//                if ("21".equals(monthType) || "31".equals(monthType)){
//
//                    CardDTO cardDTO = cardDao.getBalance(cardMonthRecordDTO.getCustomerId());
//                    if(cardDTO != null && cardHasForm.getAmount() != null && cardDTO.getCardBalance().compareTo(cardHasForm.getAmount()) >= 0){
//                        log.info("判断车牌是否有会员卡-余额充足【{}】", cardDTO.getCardBalance());
//                        Card card = new Card();
//                        card.setId(cardDTO.getId());
//                        card.setCardBalance(cardDTO.getCardBalance().subtract(outPayDataForm.getAmount()));
//                        cardDao.updateById(card);
//
//                    } else if(cardDTO != null && cardHasForm.getAmount() != null) {
//                        log.info("判断车牌是否有会员卡-余额不足【{}】，应支付【{}】", cardDTO.getCardBalance(), cardHasForm.getAmount());
//                    } else if (cardDTO != null && cardHasForm.getAmount() == null) {
//                        log.error("判断车牌是否有会员卡-应支付金额为空，不能判断会员卡");
//                    } else  {
//                        log.info("判断车牌是否有会员卡-没有会员卡");
//                    }
//                }
//            }

                /*6、如果是余额支付，则扣除余额*/
                if(orderCardTypeEnum.equals(OrderCardTypeEnum.BALANCE)){
                    for (HasCardDTO.UsedCard usedCard : hasCardDTO.getUsedCardList()) {
                        Card card = new Card();
                        card.setId(usedCard.getId());
                        card.setCardBalance(usedCard.getOldBalance().subtract(outPayDataForm.getAmount()));
                        cardDao.updateById(card);
                        log.info("预支付-扣除余额【{}】元", outPayDataForm.getAmount());
                    }
                }
            } else {
                log.error(bolianPrePayRes.getErrmsg());
                map.put("state", "0");
                map.put("errmsg", bolianPrePayRes.getErrmsg());

                //  回滚事物
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            }
        } else {
            log.info(hasCardDTO.getMassage());
            map.put("state", "0");
            map.put("errmsg", "没有会员卡，禁止放行");
        }

        return map;
    }

    /**
     * 车辆进场通知
     */
    @Override
    @Transactional
    public Map<String, Object> carInPark(String data) {
        Map<String, Object> map = Maps.newHashMap();
        //  验证签名
        JsonNode jsonNode = JsonUtil.getJsonNode(data);
        String dataStr = jsonNode.get("data").toString();
        String signStr = jsonNode.get("sign").asText();
        BoLianCarInParkDataForm form = JsonUtil.JSONToObj(dataStr, BoLianCarInParkDataForm.class);
        boolean tenpaySign = BolianUtil.isTenpaySign(dataStr, signStr);
        if (!tenpaySign) {
            log.error("验签失败");
            map.put("order_id", form.getOrder_id());
            map.put("state", 0);
            map.put("errmsg", "验签失败");
            return map;
        }
        //  根据订单号查询订单
        OrderInfoDTO orderInfoDTO = orderInfoDao.getByOrderCode(form.getOrder_id());
        if (orderInfoDTO != null) {
            log.error("重复订单号【{}】", form.getOrder_id());
            map.put("order_id", form.getOrder_id());
            map.put("state", 0);
            map.put("errmsg", "重复订单号");
            return map;
        }
        //  查询客户车牌信息
        CarDTO carDTO = carDao.getCustCarByCarNumber(form.getPlate_number());
        if (carDTO == null) {
            carDTO = new CarDTO();
        }

        //  根据泊链停车场编号获取停车场
        ParkInfoDTO parkInfoDTO = parkInfoDao.getParkByBolianParkUnionIdBolianParkId(form.getPark_union_id(), form.getPark_id());

        /*1、添加进场订单*/
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setOrderCode(form.getOrder_id());   //订单编号
        orderInfo.setCarNumber(form.getPlate_number());   //车牌号码
        orderInfo.setInMsg("正常进场");
        if (parkInfoDTO == null) {
            log.error("数据库没有该停车场，park_id暂时存为com_id：" + form.getCom_id());
            orderInfo.setParkId(Long.valueOf(form.getCom_id()));    //泊链com_id
            orderInfo.setInMsg("数据库没有该停车场，park_id暂时存为com_id：" + form.getCom_id());
        } else {
            orderInfo.setParkId(parkInfoDTO.getId());    //停车场id
            /*2、处理车牌没有正常出场的订单*/
            setNotOutOrder(form.getPlate_number(), null);
        }
        orderInfo.setInTime(DateTimeUtil.stampToTime(form.getStart_time())); //进场时间
        orderInfo.setStatus(OrderStatusEnum.IN.getCode()); //订单状态（1：进场，2：出场）
        orderInfo.setCustomerId(carDTO.getCustomerId()); //客户ID
        orderInfoDao.insert(orderInfo);

        /*3、更新预约记录状态*/
        parkReserveRecordService.updateStatusByCarNumber(form.getPlate_number(), orderInfo.getParkId(), orderInfo.getInTime(), orderInfo.getId());

        if (parkInfoDTO != null){
            /*4、修改道尔停车场车位数量*/
            if(parkInfoDTO.getParkSource().equals(3)){
                CarPortRes carPort = DaoerApiUtil.getCarPort(form.getPark_id());
                if(carPort != null){
                    ParkInfo parkInfo = new ParkInfo();
                    parkInfo.setId(parkInfoDTO.getId());
                    parkInfo.setTotalParkSpace(carPort.getBody().get(0).getTotal().getTotal());
                    parkInfo.setEmptyParkSpace(carPort.getBody().get(0).getIdle().getTotal());
                    parkInfoDao.updateById(parkInfo);
                }
            }

            String requestId = MDC.get("requestId");

            /*5、发送公众号进场通知*/
            mpMessageUtil.inParkMessage(form.getPlate_number(), parkInfoDTO.getParkName(), form.getStart_time(),requestId);
            /*6、发送周边商户优惠券*/
            shopTicketService.sendShopTicket(carDTO.getCustomerId(),parkInfoDTO.getId());
        }

        map.put("order_id", orderInfo.getId());
        map.put("customerId", carDTO.getCustomerId());
        map.put("carNumber", form.getPlate_number());
        map.put("state", 1);
        map.put("errmsg", "进场成功");
        return map;
    }

    @Override
    public boolean verifyDisplayPrePayButton(Long parkId,String carNum){
        boolean result = false;
        //判断 用户是否支持当前车场无感支付
        CardHasForm cardHasForm = new CardHasForm();
        cardHasForm.setParkId(parkId);
        cardHasForm.setCarNumber(carNum);
        HasCardDTO hasCardDTO = cardService.hasCard(cardHasForm);
        if(hasCardDTO.getOrderCardTypeEnum().getCode() != OrderCardTypeEnum.NO.getCode()) {
            result = true;
        }
        return result;
    }

    /**
     * 泊链无感支付通知结果
     */
    @Override
    @Transactional
    public Map<String, Object> noFeelOutParkNotice(String data) {
        JsonNode jsonNode = JsonUtil.getJsonNode(data);
        String dataStr = jsonNode.get("data").toString();
        BoLianNoFeelOutDataForm form = JsonUtil.JSONToObj(dataStr, BoLianNoFeelOutDataForm.class);

        Map<String,Object> map = new HashMap();

        //  根据订单号查询订单
        OrderInfoDTO orderInfoDTO = orderInfoDao.getByOrderCode(form.getOrder_id());

        //  添加订单交易记录
        OrderTradeRecord orderTradeRecord = new OrderTradeRecord();
        //  支付成功
        if(form.getState().equals(1)) {
            /*1、添加缺少的进场订单*/
            if (orderInfoDTO == null) {
                log.error("订单【{}】不存在，添加异常进场订单", form.getOrder_id());

                ParkInfoDTO parkInfoDTO = parkInfoDao.getParkByBolianComId(form.getCom_id());
                orderInfoDTO = addExceptionInOrder(form.getOrder_id(), form.getUser_id(), form.getPlate_number(), parkInfoDTO, form.getCom_id(), form.getIn_time(), form.getOut_time(), OrderStatusEnum.OUT);
            }

            /*2、校验是否有会员卡*/
            CardHasForm cardHasForm = new CardHasForm();
            cardHasForm.setParkId(orderInfoDTO.getParkId());
            cardHasForm.setCarNumber(form.getPlate_number());
            cardHasForm.setAmount(new BigDecimal(form.getMoney()));
            cardHasForm.setFreeOutTime(form.getFree_out_time()+"");
            cardHasForm.setBeginTime(DateTimeUtil.stampToTime(form.getIn_time()));
            cardHasForm.setEndTime(new Date());
            //  是否有会员卡
            HasCardDTO hasCardDTO = cardService.hasCard(cardHasForm);

            orderTradeRecord.setPayType(hasCardDTO.getOrderCardTypeEnum().getCode());   //  支付类型（1：月租卡；2：白天卡；3：夜间卡；4：余额；5：现金；99：其他）
            /*3、支付成功，如果是余额支付，则扣除余额*/
            if(hasCardDTO.getOrderCardTypeEnum().equals(OrderCardTypeEnum.BALANCE)) {
                for (HasCardDTO.UsedCard usedCard: hasCardDTO.getUsedCardList()){
                    Card card = new Card();
                    card.setId(usedCard.getId());
                    card.setCardBalance(usedCard.getOldBalance().subtract(new BigDecimal(form.getMoney())));
                    cardDao.updateById(card);
                    log.info("出场校验是否支持无感支付-扣除余额【{}】元", new BigDecimal(form.getMoney()));
                }
            }

            orderTradeRecord.setPayStatus(PayStatusEnum.PAID.getCode());    //  支付状态（0：待支付，1：已支付，2：取消支付）
            orderTradeRecord.setPayMsg("成功");   //  支付说明
        }
        //  支付失败
        if(!form.getState().equals(1)) {
            orderTradeRecord.setPayStatus(PayStatusEnum.FAIL.getCode());    //  支付状态（0：待支付，1：已支付，2：取消支付）
            orderTradeRecord.setPayMsg(form.getRemark());   //  支付说明
        }
        orderTradeRecord.setOrderCode(form.getOrder_id()); //  订单号
        orderTradeRecord.setTradeNo(form.getTrade_no());   //  交易流水号
        orderTradeRecord.setPayableAmount(new BigDecimal(form.getMoney()));  //  应付金额
        orderTradeRecord.setPaidAmount(new BigDecimal(form.getMoney()));   //  实付金额
        orderTradeRecord.setPayTime(new Date());    //  支付时间
        orderTradeRecord.setTradeType(2);   //  交易类型（1：预支付，2：出场支付，3：不确定（补录的交易记录））
        orderTradeRecord.setPayMsg("出场支付");
        orderTradeRecordDao.insert(orderTradeRecord);

        map.put("com_id", form.getCom_id());
        map.put("trade_no", form.getTrade_no());
        map.put("state", "1");
        map.put("errmsg", "处理完成");
        return map;
    }

    /**
     * 车辆出场通知
     */
    @Override
//    @Transactional(isolation = Isolation.READ_COMMITTED)    //  READ_COMMITTED读已提交，不可重读
    @Transactional
    public Map<String, Object> carOutPark(String data) {
        Map<String, Object> map = Maps.newHashMap();

        JsonNode jsonNode = JsonUtil.getJsonNode(data);
        String dataStr = jsonNode.get("data").toString();
        String signStr = jsonNode.get("sign").asText();
        BoLianCarOutParkDataForm form = JsonUtil.JSONToObj(dataStr, BoLianCarOutParkDataForm.class);
        if (!BolianUtil.isTenpaySign(dataStr, signStr)) {
            log.error("车辆出场通知验签失败");
            map.put("order_id", form.getOrder_id());
            map.put("state", 0);
            map.put("errmsg", "验签失败");
            return map;
        }

        log.info("出场通知-车牌【{}】离开停车场【{}】", form.getPlate_number(), form.getPark_name());

        //  出场时间
        Long end_time = form.getEnd_time() * 1000;
        //  根据泊链停车场编号获取停车场
        ParkInfoDTO parkInfoDTO = parkInfoDao.getParkByBolianParkUnionIdBolianParkId(form.getPark_union_id(), form.getPark_id());
        //  根据订单号查询订单
        OrderInfoDTO orderInfoDTO = orderInfoDao.getByOrderCode(form.getOrder_id());

        /*1、添加缺少的进场订单*/
        if (orderInfoDTO == null) {
            log.error("订单【{}】不存在，添加异常进场订单", form.getOrder_id());
            Long inTime =  form.getIn_time() == null ? new Date().getTime() : form.getIn_time();
            orderInfoDTO = addExceptionInOrder(form.getOrder_id(), form.getUser_id(), form.getPlate_number(), parkInfoDTO, form.getCom_id(), inTime, end_time, OrderStatusEnum.OUT);
        }

        /*2、修改订单状态*/
        updateOrderStatus(orderInfoDTO.getId(), orderInfoDTO.getInTime().getTime(), end_time, Long.valueOf(form.getUser_id()), form.getPay_type(), form.getTrade_no(), form.getMoney());

        if (parkInfoDTO != null) {
            /*3、（异步）校准泊链与中萌的交易记录，将中萌欠缺的交易记录补充完善*/
            orderInfoService.calibrateHistoryData(orderInfoDTO.getOrderCode(), parkInfoDTO.getBolianParkUnionId(), parkInfoDTO.getBolianParkId(), parkInfoDTO.getParkSource(), form.getPlate_number(), form.getPay_type(), 2);

            /*4、处理车牌没有正常出场的订单*/
            setNotOutOrder(form.getPlate_number(), form.getPay_type());

            /*5、修改停车场车位数量*/
            //  道尔停车场
            if(parkInfoDTO.getParkSource().equals(3)){
                CarPortRes carPort = DaoerApiUtil.getCarPort(form.getPark_id());
                if(carPort != null) {
                    ParkInfo parkInfo = new ParkInfo();
                    parkInfo.setId(parkInfoDTO.getId());
                    parkInfo.setTotalParkSpace(carPort.getBody().get(0).getTotal().getTotal());
                    parkInfo.setEmptyParkSpace(carPort.getBody().get(0).getIdle().getTotal());
                    parkInfoDao.updateById(parkInfo);
                }
            }

            String requestId = MDC.get("requestId");

            /*6、微信公众号发送离场通知*/
            mpMessageUtil.monthAndDurationMessage(orderInfoDTO.getOrderCode(), orderInfoDTO.getInTime().getTime(), end_time, form.getMoney(), requestId);
        }

        map.put("order_id", form.getOrder_id());
        map.put("customerId", form.getUser_id());
        map.put("carNumber", form.getPlate_number());
        map.put("state", 1);
        map.put("errmsg", "处理完毕");
        return map;
    }

    /**
     * 校准泊链与中萌的交易记录，将中萌欠缺的交易记录补充完善
     * @param carOutPayType 泊链出场支付类型（0：现金，1：电子支付，2：重复入场）
     */
    @Override
    @Async
    public BolianOrderHistoryRes calibrateHistoryData(String orderCode, String bolianParkUnionId, String bolianParkId, Integer parkSource, String carNum, Integer carOutPayType, Integer tradeType){
        //  查询泊链历史订单
        Long startTime = DateTimeUtil.getStartOfDay(DateTimeUtil.getDaysAgoDate(0)).getTime()/1000; //  当天0点
        Long endTime = DateTimeUtil.getEndOfDay(new Date()).getTime()/1000; //  当天最后1秒
        BolianOrderHistoryRes bolianOrderHistoryRes = BolianUtil.queryOrders(orderCode, bolianParkUnionId, bolianParkId, carNum,startTime,endTime,0,30);
        //  根据订单号查询订单交易记录
        List<OrderTradeRecordDTO> orderTradeRecordDTOList = orderTradeRecordDao.getOrderTradeRecordByOrderCode(orderCode);
        //  泊链交易流水号
        List<String> bolianTradeNoList = bolianOrderHistoryRes.getData().stream().map(BolianOrderHistoryRes.OrderHistoryData::getTrade_no).collect(Collectors.toList());
        //  立码停车交易流水号
        List<String> tradeRecordTradeNoList = orderTradeRecordDTOList.stream().map(OrderTradeRecordDTO::getTradeNo).collect(Collectors.toList());

        /*1、如果没有支付记录，查询该车牌是否在停车场有月租*/
        if(bolianTradeNoList.size() == 0){
            //  道尔停车场月租车基本信息
            if(parkSource.equals(3)){
                MonthlyCarInfoRes monthlycar = DaoerApiUtil.getMonthlycar(bolianParkId, carNum);
                if(monthlycar !=null && monthlycar.getHead().getStatus().equals(1)){
                    LambdaUpdateWrapper<OrderInfo> updateWrapper = new LambdaUpdateWrapper<>();
                    updateWrapper.eq(OrderInfo::getOrderCode, orderCode);
                    OrderInfo orderInfo = new OrderInfo();
                    orderInfo.setIsParkMonthCar(1); //  是否在停车场有月租（0：没有，1：有）
                    orderInfo.setParkMonthStartTime(DateTimeUtil.fomatDateTime(monthlycar.getBody().getStartTime()));
                    orderInfo.setParkMonthDueTime(DateTimeUtil.fomatDateTime(monthlycar.getBody().getEndTime()));
                    orderInfoDao.update(orderInfo, updateWrapper);
                }
            }
        }

        /*2、修改状态*/
        //  交集（list1集合只保留list2中存在的元素），泊链与立码停车都有的交易记录
        tradeRecordTradeNoList.retainAll(bolianTradeNoList);
        for(OrderTradeRecordDTO tradeRecordDTO: orderTradeRecordDTOList){
            //  将泊链已经支付成功，但立码停车交易记录为未支付的，改为已支付
            if(tradeRecordTradeNoList.contains(tradeRecordDTO.getTradeNo()) && !tradeRecordDTO.getPayStatus().equals(PayStatusEnum.PAID.getCode())){
                OrderTradeRecord orderTradeRecord = new OrderTradeRecord();
                orderTradeRecord.setId(tradeRecordDTO.getId());
                orderTradeRecord.setPaidAmount(tradeRecordDTO.getPayableAmount());   //  实付金额
                orderTradeRecord.setPayStatus(PayStatusEnum.PAID.getCode());
                orderTradeRecord.setPayMsg("校准-修改交易状态");
                orderTradeRecordDao.updateById(orderTradeRecord);
            }
        }

        /*3、添加交易记录*/
        //  差集 a-b（list1集合只保留list2中没有的元素），泊链有，立码停车没有的交易记录
        bolianTradeNoList.removeAll(tradeRecordTradeNoList);
        for(BolianOrderHistoryRes.OrderHistoryData historyTradeNo: bolianOrderHistoryRes.getData()) {
            //  如果中萌缺少交易记录，则添加
            if (bolianTradeNoList.contains(historyTradeNo.getTrade_no())) {
                OrderTradeRecord orderTradeRecord = new OrderTradeRecord();
                orderTradeRecord.setOrderCode(orderCode); //  订单号
                orderTradeRecord.setTradeNo(historyTradeNo.getTrade_no());   //  交易流水号
                orderTradeRecord.setPayableAmount(new BigDecimal(historyTradeNo.getMoney()));  //  应付金额
                orderTradeRecord.setPaidAmount(new BigDecimal(historyTradeNo.getMoney()));   //  实付金额
                orderTradeRecord.setPayTime(DateTimeUtil.stampToTime(historyTradeNo.getIn_time()));    //  支付时间
                orderTradeRecord.setPayStatus(PayStatusEnum.PAID.getCode());
                orderTradeRecord.setPayMsg("校准-补录交易记录");
                orderTradeRecord.setPayType(99);   //  支付类型（1：月租卡；2：白天卡；3：夜间卡；4：余额；5：现金；99：其他）
                //  由于无感支付没有补录到此交易记录，所以应该为出场支付
                orderTradeRecord.setTradeType(tradeType == null ? 3 : tradeType);   //  交易类型（1：预支付，2：出场支付，3：不确定（补录的交易记录））
                orderTradeRecordDao.insert(orderTradeRecord);
            }
        }

        return bolianOrderHistoryRes;
    }

    /**
     * 处理车牌没有正常出场的订单
     */
    @Override
    @Async
    public void setNotOutOrder(String carNumber, Integer carOutPayType) {
        //  根据车牌号查询未出场订单
        List<OrderInfoDTO> orderInfoDTOList = orderInfoDao.getNotOutOrderByCarNumber(carNumber);

        //  设置异常出场时间
        for (OrderInfoDTO orderInfoDTO : orderInfoDTOList) {
            ParkInfo parkInfo = parkInfoDao.selectById(orderInfoDTO.getParkId());
            BolianOrderHistoryRes historyRes = null;
            if(parkInfo != null){
                //  校准泊链与中萌的交易记录，将中萌欠缺的交易记录补充完善
                historyRes = calibrateHistoryData(orderInfoDTO.getOrderCode(), parkInfo.getBolianParkUnionId(), parkInfo.getBolianParkId(), parkInfo.getParkSource(), carNumber, carOutPayType, null);
            }

            OrderInfo orderInfo = new OrderInfo();
            orderInfo.setId(orderInfoDTO.getId());  //  订单id
            if(historyRes != null && historyRes.getData() != null && historyRes.getData().size() != 0){
                orderInfo.setOutTime(DateTimeUtil.stampToTime(historyRes.getData().get(0).getIn_time()));   //  出场时间
                orderInfo.setOutMsg("没有正常出场记录，后期补录");
            } else {
                orderInfo.setOutTime(new Date());   //  出场时间
                orderInfo.setOutMsg("没有正常出场记录，暂时将出场时间记录为当前时间" + DateTimeUtil.getTime());
            }
            orderInfo.setOutType(OutTypeEnum.EXCEPTION.getCode());  //  出场类型（1：正常出场，2：异常出场）
            orderInfo.setStatus(OrderStatusEnum.OUT.getCode()); //  订单状态（1：进场，2：出场）
            orderInfoDao.updateById(orderInfo);

            log.info("处理车牌【{}】没有正常出场的订单", orderInfoDTO.getOrderCode());
        }
    }

    /**
     * 添加缺少的进场订单
     */
    OrderInfoDTO addExceptionInOrder(String orderCode, String customerId, String carNumber, ParkInfoDTO parkInfoDTO, String com_id, Long inTime, Long outTime, OrderStatusEnum statusEnum) {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setOrderCode(orderCode);   //  订单编号
        orderInfo.setCarNumber(carNumber);   //  车牌号码
        orderInfo.setCustomerId(customerId == null ? null : Long.parseLong(customerId));
        if (parkInfoDTO == null) {
            orderInfo.setParkId(Long.valueOf(com_id));    //  泊链com_id
            orderInfo.setInMsg("数据库没有该停车场，停车场id暂存泊链com_id【" + com_id + "】");
        } else {
            orderInfo.setParkId(parkInfoDTO.getId());    //  停车场id
            orderInfo.setInMsg("没有进场记录，后期补录");
        }
        orderInfo.setInTime(DateTimeUtil.stampToTime(inTime)); //  进场时间
        orderInfo.setOutTime(statusEnum.equals(OrderStatusEnum.OUT) ? DateTimeUtil.stampToTime(outTime) : null);    //  出场时间
        orderInfo.setOutType(statusEnum.equals(OrderStatusEnum.OUT) ? 1 : null); //  出场类型（1：正常出场，2：异常出场）
        orderInfo.setOutMsg(statusEnum.equals(OrderStatusEnum.OUT) ? "正常出场" : null);
        orderInfo.setStatus(statusEnum.getCode()); //  订单状态（1：进场，2：出场）
        orderInfoDao.insert(orderInfo);

        OrderInfoDTO orderInfoDTO = new OrderInfoDTO();
        BeanUtils.copyProperties(orderInfo, orderInfoDTO);
        return orderInfoDTO;
    }

    /**
     * 修改订单状态
     */
    @Override
    @Async
    public void updateOrderStatus(Long orderId, Long inTime, Long outTime, Long customerId, Integer outPayType, String tradeNo, String outPayMoney) {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId(orderId);  //  订单id
        orderInfo.setOutTime(new Date(outTime));   //  出场时间
        orderInfo.setCustomerId(customerId);   //  客户id
        orderInfo.setOutType(1);  //  出场类型（1：正常出场，2：异常出场）
        orderInfo.setOutMsg("正常出场");
        orderInfo.setOutPayType(outPayType);
        orderInfo.setStatus(OrderStatusEnum.OUT.getCode()); //  订单状态（1：已入场待支付，2：已支付待出场，3：已支付已出场，4：未支付，已出场）
        //  时间戳（转分钟）
        long parkDuration = outTime / 1000 / 60 - inTime / 1000 / 60;
        orderInfo.setParkDuration(parkDuration);
        orderInfo.setOutPayMoney(new BigDecimal(outPayMoney));  //  出场时支付金额

        orderInfoDao.updateById(orderInfo);
    }



}
